﻿namespace NTLib.Pipe.Network.UTest.Tcp
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using NTLib.Pipe.Network.Descriptors;
    using NTLib.Pipe.Targets;
    using NTLib.Core.Tasks;
    using NTLib.Core.Win.Net;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Test.Resources;
    using Containers;
    using Network.Connectors;
    using System.Net;
    using Formats;
    using System.Threading;
    using Pipe.Exceptions;
    using Core.Exceptions.Component;
    using System.Diagnostics;
    using Streaming.Formats;

    [TestClass]
    public class NetworkPipeSourceUTest : BaseTestClass
    {
        #region Methods

        // TODO : Network pipe source must be test after Connector and formater test
        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void NetworkPipeSource_Creation()
        {
            throw new NotImplementedException();
            //CancellationTokenSource timeout = CreateTimeout(15);

            //using (var locker = base.LockTest(timeout.Token))
            //{
            //    ConnectorManager.Instance.Clear();
            //    PackageFormatManager.Instance.Clear();

            //    DataStruct tester = new DataStruct();

            //    var ipaddr = IPAddress.Loopback;
            //    var port = ipaddr.GetFreePort();
            //    Uri addr = new Uri("Tcp://" + ipaddr.ToString() + ":" + port, UriKind.RelativeOrAbsolute);
            //    NetworkTargetDescription targetH = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, true, Guid.NewGuid());
            //    NetworkTargetDescription target = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, false, Guid.NewGuid());

            //    using (NetworkPipeSource<DataStruct> pipeOrigin = new NetworkPipeSource<DataStruct>())
            //    {
            //        Assert.AreEqual(pipeOrigin.Status, PipeStatus.NotInitialize);

            //        try
            //        {
            //            pipeOrigin.SendItemAsync(tester).Wait(timeout.Token);
            //        }
            //        catch (Exception ex)
            //        {
            //            Assert.IsTrue(ExceptionTest<PipeNotInitializedException>(ex));
            //        }

            //        var taskHostWait = pipeOrigin.InitializeAsync(targetH, PipeType.TwoWay);

            //        using (NetworkPipeSource<DataStruct> pipeTarget = new NetworkPipeSource<DataStruct>())
            //        {
            //            Assert.AreEqual(pipeTarget.Status, PipeStatus.NotInitialize);

            //            try
            //            {
            //                pipeTarget.SendItemAsync(tester).Wait(timeout.Token);
            //            }
            //            catch (Exception ex)
            //            {
            //                Assert.IsTrue(ExceptionTest<PipeNotInitializedException>(ex));
            //            }

            //            pipeTarget.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);

            //            taskHostWait.Wait(timeout.Token);

            //            Assert.IsTrue(pipeOrigin.CanPopItem);
            //            Assert.IsTrue(pipeOrigin.CanPushItem);

            //            Assert.IsTrue(pipeTarget.CanPopItem);
            //            Assert.IsTrue(pipeTarget.CanPushItem);

            //            Assert.AreEqual(pipeOrigin.Status, PipeStatus.Ready);
            //            Assert.AreEqual(pipeTarget.Status, PipeStatus.Ready);
            //        }
            //    }
            //}
        }

        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void NetworkPipeSource_Creation_with_wronginfo()
        {
            //CancellationTokenSource timeout = CreateTimeout(15);

            //using (var locker = base.LockTest(timeout.Token))
            //{
            //    ConnectorManager.Instance.Clear();
            //    FormatManager.Instance.Clear();

            //    var ipaddr = IPAddress.Loopback;
            //    var port = ipaddr.GetFreePort();
            //    Uri addr = new Uri("Tcp://" + ipaddr.ToString() + ":" + port, UriKind.RelativeOrAbsolute);

            //    NetworkTargetDescription target = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, true, Guid.NewGuid(), false);

            //    using (NetworkPipeSource<DataStruct> pipeOrigin = new NetworkPipeSource<DataStruct>())
            //    {
            //        try
            //        {
            //            pipeOrigin.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);
            //            Assert.Fail("An exeception sould be send to explains that the connector doesn't exist");
            //        }
            //        catch (AggregateException ex)
            //        {
            //            if (!(ex.InnerException is ComponentNotRegisteredException))
            //                throw;
            //        }

            //        ConnectorManager.Instance.Register<TcpConnector>();

            //        try
            //        {
            //            pipeOrigin.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);
            //            Assert.Fail("An exeception sould be send to explains that the connector doesn't exist");
            //        }
            //        catch (AggregateException ex)
            //        {
            //            if (!(ex.InnerException is ComponentNotRegisteredException))
            //                throw;
            //        }

            //        FormatManager.Instance.Register<SimpleJsonFormatter>();

            //        try
            //        {
            //            pipeOrigin.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);
            //        }
            //        catch (TaskCanceledException)
            //        {
            //        }
            //    }
            //}
            throw new NotImplementedException();
        }

        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void NetworkPipe_PushAndPop()
        {
            //CancellationTokenSource timeout = CreateTimeout(15);

            //using (var locker = base.LockTest(timeout.Token))
            //{
            //    DataStruct tester = new DataStruct();

            //    var ipaddr = IPAddress.Loopback;
            //    var port = ipaddr.GetFreePort();
            //    Uri addr = new Uri("Tcp://" + ipaddr.ToString() + ":" + port, UriKind.RelativeOrAbsolute);
            //    NetworkTargetDescription targetH = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, true, Guid.NewGuid());
            //    NetworkTargetDescription target = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, false, Guid.NewGuid());

            //    using (NetworkPipeSource<DataStruct> pipeOrigin = new NetworkPipeSource<DataStruct>())
            //    {

            //        var taskHostWait = pipeOrigin.InitializeAsync(targetH, PipeType.TwoWay);

            //        using (NetworkPipeSource<DataStruct> pipeTarget = new NetworkPipeSource<DataStruct>())
            //        {
            //            pipeTarget.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);

            //            taskHostWait.Wait(timeout.Token);

            //            tester.Msg = "poney";
            //            tester.Score = 42;

            //            TaskCompletionSource<object> waiter = new TaskCompletionSource<object>();

            //            pipeOrigin.PushItemAsync<object>(tester, waiter, timeout.Token).Wait(timeout.Token);
            //            var received = pipeTarget.DequeueItemAsync(timeout.Token).WaitResult(timeout.Token);

            //            //TODO: Found a way to have a read confirmation from the other side.
            //            // Possible solution : make a tunel protocol that like tcp confirme that the package is read on the other side.
            //            Assert.IsTrue(waiter.Task.IsCompleted);

            //            Assert.AreEqual(tester.Msg, received.Msg);
            //            Assert.AreEqual(tester.Score, received.Score);
            //        }
            //    }
            //}
            throw new NotImplementedException();
        }

        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void NetworkPipe_PushAndPop_WithCancel()
        {
            throw new NotImplementedException();
        }


        [TestCategory("Pipe.Source")]
        [TestMethod]
        public void NetworkPipe_SendAndPop()
        {
            //CancellationTokenSource timeout = CreateTimeout(15);

            //using (var locker = base.LockTest(timeout.Token))
            //{
            //    ConnectorManager.Instance.Clear();
            //    FormatManager.Instance.Clear();

            //    DataStruct tester = new DataStruct();

            //    var ipaddr = IPAddress.Loopback;
            //    var port = ipaddr.GetFreePort();
            //    Uri addr = new Uri("Tcp://" + ipaddr.ToString() + ":" + port, UriKind.RelativeOrAbsolute);
            //    NetworkTargetDescription targetH = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, true, Guid.NewGuid());
            //    NetworkTargetDescription target = NetworkTargetDescription.Create<TcpConnector, SimpleJsonFormatter>(addr, false, Guid.NewGuid());

            //    using (NetworkPipeSource<DataStruct> pipeOrigin = new NetworkPipeSource<DataStruct>())
            //    {
            //        pipeOrigin.InitializeAsync(targetH, PipeType.TwoWay).Wait(timeout.Token);

            //        using (NetworkPipeSource<DataStruct> pipeTarget = new NetworkPipeSource<DataStruct>())
            //        {
            //            pipeTarget.InitializeAsync(target, PipeType.TwoWay).Wait(timeout.Token);


            //            tester.Msg = "poney";
            //            tester.Score = 42;

            //            pipeOrigin.SendItemAsync(tester, timeout.Token).Wait(timeout.Token);
            //            var received = pipeTarget.DequeueItemAsync(timeout.Token).WaitResult(timeout.Token);

            //            Assert.AreEqual(tester.Msg, received.Msg);
            //            Assert.AreEqual(tester.Score, received.Score);
            //        }

            //        try
            //        {
            //            pipeOrigin.SendItemAsync(tester, timeout.Token).Wait(timeout.Token);
            //        }
            //        catch (Exception ex)
            //        {
            //            Debug.WriteLine(ex.Message);
            //        }
            //    }
            //}
            throw new NotImplementedException();
        }

        #endregion
    }
}
